package com.ty.user.starter.advice;

import com.ty.user.starter.annon.Decrypt;
import com.ty.user.starter.exception.UserMgtException;
import com.ty.user.starter.util.RSAUtil;
import com.ty.user.starter.util.SessionUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.beanutils.PropertyUtils;
import org.jetbrains.annotations.NotNull;
import org.springframework.core.MethodParameter;
import org.springframework.http.HttpInputMessage;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.util.Assert;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.servlet.mvc.method.annotation.RequestBodyAdvice;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Type;
import java.security.KeyPair;

/**
 * 数据接收请求处理器
 * 只对{@link Decrypt}注解进行解密操作
 * 注意,此处理器只对{@link org.springframework.web.bind.annotation.RequestBody}注解有效
 *
 * @see org.springframework.web.bind.annotation.RequestBody
 */
@Slf4j
@RestControllerAdvice
public class DecryptRequestBodyAdvice implements RequestBodyAdvice {
    @Override
    public boolean supports(MethodParameter methodParameter, @NotNull Type type, @NotNull Class<? extends HttpMessageConverter<?>> aClass) {
        return methodParameter.getParameter().getAnnotation(Decrypt.class) != null;
    }

    @Override
    public @NotNull HttpInputMessage beforeBodyRead(@NotNull HttpInputMessage httpInputMessage, @NotNull MethodParameter methodParameter, @NotNull Type type, @NotNull Class<? extends HttpMessageConverter<?>> aClass) {
        return httpInputMessage;
    }

    @Override
    public @NotNull Object afterBodyRead(@NotNull Object o, @NotNull HttpInputMessage httpInputMessage, MethodParameter methodParameter, @NotNull Type type, @NotNull Class<? extends HttpMessageConverter<?>> aClass) {
        Decrypt decode = methodParameter.getParameter().getAnnotation(Decrypt.class);
        String[] fields = decode.field();
        KeyPair keyPair = (KeyPair) SessionUtils.getSession().getAttribute(SessionUtils.RSA_KEY);
        Assert.notNull(keyPair, "请先获取公钥");
        for (String field : fields) {
            try {
                Object property = PropertyUtils.getProperty(o, field);
                Assert.isTrue(property instanceof String, "解密字段必须为字符串类型");
                // 字符串解密
                byte[] bytes = RSAUtil.decrypt(RSAUtil.decodeBase64((String) property), keyPair.getPrivate());
                PropertyUtils.setProperty(o, field, new String(bytes));
            } catch (InvocationTargetException | IllegalAccessException | NoSuchMethodException | UserMgtException e) {
                log.error("字符解密失败", e);
                throw new IllegalArgumentException(String.format("属性: %s 解密失败", field));
            }
        }
        return o;
    }

    @Override
    public Object handleEmptyBody(Object o, @NotNull HttpInputMessage httpInputMessage, @NotNull MethodParameter methodParameter, @NotNull Type type, @NotNull Class<? extends HttpMessageConverter<?>> aClass) {
        return o;
    }
}
